from flask import Flask, request, jsonify
from apscheduler.triggers.cron import CronTrigger
import logging
import threading
import time
import pymysql
import uuid
from datetime import datetime
import os
import requests
from cryptography.fernet import Fernet
from flask_cors import CORS

app = Flask(__name__)
CORS(app, resources={r"/*": {"origins": ["http://119.45.166.189:9003"]}})

# ========== 日志设置 ==========
logging.basicConfig(
    level=logging.INFO,
    format='[%(asctime)s] %(levelname)s in %(module)s: %(message)s'
)
logger = logging.getLogger(__name__)

# ========== 加载加密配置 ==========
ENCRYPTION_KEY = os.getenv("ENCRYPTION_KEY", "your-32-byte-base64-key")
cipher = Fernet(ENCRYPTION_KEY)

def decrypt(cipher_text):
    return cipher.decrypt(cipher_text.encode()).decode()

# ========== 数据库配置 ==========
DB_CONFIG = {
    'host': decrypt(os.getenv("DB_HOST_ENC", "ZGVmYXVsdF9ob3N0")),
    'user': decrypt(os.getenv("DB_USER_ENC", "ZGVmYXVsdF91c2Vy")),
    'password': decrypt(os.getenv("DB_PASS_ENC", "ZGVmYXVsdF9wYXNz")),
    'database': "ESG_Onchain",
    'cursorclass': pymysql.cursors.DictCursor
}

CHAIN_URL = os.getenv("CHAIN_URL", "http://119.45.166.189:8099/audit-server/chain/batchInvoke")

# ========== 上链函数 ==========
def log_schedule(batch_no, trigger_type, status, record_count, tx_id, message, start_time, end_time):
    try:
        conn = pymysql.connect(**DB_CONFIG)
        with conn.cursor() as cursor:
            sql = """
            INSERT INTO onchain_schedule_log
            (batch_no, trigger_type, status, record_count, tx_id, message, start_time, end_time, created_at)
            VALUES (%s, %s, %s, %s, %s, %s, %s, %s, NOW())
            """
            cursor.execute(sql, (
                batch_no, trigger_type, status, record_count, tx_id, message, start_time, end_time
            ))
            conn.commit()
    except Exception as e:
        logging.error(f"[LOG] 写入调度日志失败: {e}")
    finally:
        conn.close()

def log_chain_result(batch_no, result):
    try:
        conn = pymysql.connect(**DB_CONFIG)
        with conn.cursor() as cursor:
            sql = """
            INSERT INTO indicators_onchain_result
            (batch_no, tx_id, chain_id, tx_code, tx_message, contract_code, contract_result,
             contract_message, credible_time, data_id, module_id, rule_id, rule_version,
             audit_status, audit_message, enable_sensitive, create_time)
            VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, NOW())
            """
            data = result["data"]
            chain = data["chain"]
            audit = data["audit"]
            cursor.execute(sql, (
                batch_no,
                chain.get("txId"), data.get("chainId"), chain.get("txCode"), chain.get("txMessage"),
                chain.get("contractCode"), chain.get("contractResult"), chain.get("contractMessage"),
                data.get("credibleTime"), data.get("dataId"), data.get("moduleId"), data.get("ruleId"),
                data.get("ruleVersion"), audit.get("status"), audit.get("message"), audit.get("enableSensitive")
            ))
            conn.commit()
    except Exception as e:
        logging.error(f"[CHAIN] 上链响应结果写入失败: {e}")
    finally:
        conn.close()

def get_indicators(offset=0, batch_size=100):
    try:
        conn = pymysql.connect(**DB_CONFIG)
        with conn.cursor() as cursor:
            cursor.execute("SELECT * FROM indicators LIMIT %s OFFSET %s", (batch_size, offset))
            return cursor.fetchall()
    except Exception as e:
        logging.error(f"[DB] 查询指标数据失败: {e}")
        return []
    finally:
        conn.close()

def push_to_chain(trigger_type='auto'):
    offset = 0
    batch_index = 1
    while True:
        data_batch = get_indicators(offset=offset)
        if not data_batch:
            logging.info("[CHAIN] 所有指标已处理完毕")
            break

        batch_no = datetime.now().strftime("%Y%m%d%H%M%S") + f"_{batch_index:02d}_" + str(uuid.uuid4())[:8]
        start_time = datetime.now()

        try:
            payload = {
                "baseInfo": {
                    "chainId": "ZW_C845899_001",
                    "contractName": "ESGTestContract",
                    "methodName": "save"
                },
                "rawData": [
                    {
                        "bizIndex": item["indicatorid"],
                        "bizData": item
                    } for item in data_batch
                ]
            }

            headers = {"Content-Type": "application/json"}
            response = requests.post(CHAIN_URL, json=payload, headers=headers)
            result = response.json()

            tx_id = result.get("data", {}).get("chain", {}).get("txId", None)
            message = result.get("message", "无返回信息")
            status = 'success' if result.get("code") == "200" else 'fail'

            log_chain_result(batch_no, result)
            log_schedule(batch_no, trigger_type, status, len(data_batch), tx_id, message, start_time, datetime.now())

            logging.info(f"[{trigger_type.upper()}] 批次 {batch_index} 完成，状态: {status}")
        except Exception as e:
            log_schedule(batch_no, trigger_type, 'fail', 0, None, str(e), start_time, datetime.now())
            logging.error(f"[{trigger_type.upper()}] 批次 {batch_index} 推送失败: {e}")

        offset += len(data_batch)
        batch_index += 1
        time.sleep(1)

# ========== APScheduler 设置 ==========
if os.getenv("ENABLE_SCHEDULER", "false") == "true":
    from apscheduler.schedulers.background import BackgroundScheduler
    scheduler = BackgroundScheduler()
    scheduler.add_job(func=batch_push_job, trigger="cron", month='1', day='1', hour=0, minute=0)
    scheduler.start()

# ========== 接口：手动触发上链 ==========
@app.route("/api/onchain", methods=["POST"])
def manual_trigger():
    threading.Thread(target=push_to_chain, kwargs={'trigger_type': 'manual'}).start()
    return jsonify({"status": "started", "message": "已手动触发上链任务"})

# ========== 接口：查询调度日志 ==========
@app.route("/api/schedule/logs", methods=["GET"])
def query_schedule_logs():
    start = request.args.get("start")
    end = request.args.get("end")
    try:
        conn = pymysql.connect(**DB_CONFIG)
        with conn.cursor() as cursor:
            sql = "SELECT * FROM onchain_schedule_log"
            params = []
            conditions = []
            if start:
                conditions.append("start_time >= %s")
                params.append(start + " 00:00:00")
            if end:
                conditions.append("end_time <= %s")
                params.append(end + " 23:59:59")
            if conditions:
                sql += " WHERE " + " AND ".join(conditions)
            sql += " ORDER BY start_time DESC LIMIT 100"
            cursor.execute(sql, tuple(params))
            rows = cursor.fetchall()
            return jsonify(rows)
    except Exception as e:
        return jsonify({"status": "error", "message": str(e)})
    finally:
        conn.close()

# ========== 接口：查询上链结果 ==========
@app.route("/api/onchain/results", methods=["GET"])
def query_onchain_results():
    try:
        page = int(request.args.get("page", 1))
        size = int(request.args.get("size", 10))
        offset = (page - 1) * size
        start = request.args.get("start")
        end = request.args.get("end")

        conn = pymysql.connect(**DB_CONFIG)
        with conn.cursor() as cursor:
            sql = """
                SELECT batch_no, tx_id, chain_id, tx_code, tx_message, contract_code,
                       contract_result, contract_message, credible_time,
                       data_id, module_id, rule_id, rule_version,
                       audit_status, audit_message, enable_sensitive, create_time
                FROM indicators_onchain_result
            """
            count_sql = "SELECT COUNT(*) as total FROM indicators_onchain_result"
            conditions = []
            params = []

            if start:
                conditions.append("create_time >= %s")
                params.append(start + " 00:00:00")
            if end:
                conditions.append("create_time <= %s")
                params.append(end + " 23:59:59")

            if conditions:
                where_clause = " WHERE " + " AND ".join(conditions)
                sql += where_clause
                count_sql += where_clause

            sql += " ORDER BY create_time DESC LIMIT %s OFFSET %s"
            params += [size, offset]

            cursor.execute(sql, tuple(params))
            rows = cursor.fetchall()

            cursor.execute(count_sql, tuple(params[:-2]))  # 查询总数
            total = cursor.fetchone()["total"]

        return jsonify({"data": rows, "total": total})
    except Exception as e:
        return jsonify({"status": "error", "message": str(e)})
    finally:
        conn.close()

# ========== 接口：查询ESG指标 ==========
@app.route("/api/indicators", methods=["GET"])
def query_indicators():
    try:
        page = int(request.args.get("page", 1))
        size = int(request.args.get("size", 10))
        offset = (page - 1) * size

        conn = pymysql.connect(**DB_CONFIG)
        with conn.cursor() as cursor:
            # 构建基础SQL
            sql = "SELECT * FROM indicators"
            count_sql = "SELECT COUNT(*) as total FROM indicators"
            params = []

            sql += " LIMIT %s OFFSET %s"
            params += [size, offset]

            cursor.execute(sql, tuple(params))
            rows = cursor.fetchall()

            cursor.execute(count_sql, tuple(params[:-2]))  # 查询总数，不传分页参数
            total = cursor.fetchone()["total"]

        return jsonify({"data": rows, "total": total})
    except Exception as e:
        return jsonify({"status": "error", "message": str(e)})
    finally:
        conn.close()

# ========== 接口：健康检查 ==========
@app.route('/health')
def health():
    logger.info("健康检查通过")
    return jsonify({"status": "ok"})

# ========== 临时访问令牌 ==========
import string
import random

# 🔐 生成访问令牌
ACCESS_TOKEN = ''.join(random.choices(string.ascii_letters + string.digits, k=64))
logger.info(f"🔐 本次服务访问口令为：{ACCESS_TOKEN}")

from flask import request, jsonify

@app.route("/api/token/verify", methods=["POST"])
def verify_token():
    data = request.get_json()
    if not data or data.get("token") != ACCESS_TOKEN:
        return jsonify({"success": False, "message": "Token 无效"}), 403
    return jsonify({"success": True})